package com.dingup.paike.service.message.impl;

import com.dingup.api.model.classInfo.ClassInfo;
import com.dingup.paike.bean.aobean.ClassCurriculumAO;
import com.dingup.paike.bean.aobean.SubjectMessageAO;
import com.dingup.paike.bean.aobean.TeacherCurriculumAO;
import com.dingup.paike.bean.dtobean.PkLessonInfoDTO;
import com.dingup.paike.bean.querybean.CurriculumScheduleQuery;
import com.dingup.paike.dao.PkLessonInfoMapper;
import com.dingup.paike.enums.LessonStatusEnum;
import com.dingup.paike.enums.PkLessonUserTypeEnum;
import com.dingup.paike.feign.client.classInfo.ClassInfoFeignClient;
import com.dingup.paike.service.message.CurriculumScheduleService;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.dingup.api.Util.CalendarUtil.*;
import static com.dingup.api.Util.DateUtil.date2Str;
import static com.dingup.api.Util.DateUtil.getFormatWeek;

/**
 * Created by wancheng on 2018/10/24.
 */
@Service
public class CurriculumScheduleServiceImpl implements CurriculumScheduleService {


    private static final String FORMAT_DAY = "MM月dd日";
    private static final String FORMAT_MINUTE = "HH:mm";
    private static final String DAY_TIME_MORNING = "morning";
    private static final String DAY_TIME_AFTERNOON = "afternoon";
    private static final String DAY_TIME_NIGHT = "night";
    private static final String[] CURRICULUM_COLOR = {"#E06565", "#E4DC3F", "#39D8C7", "#714AEE", "#FF7FB9", "#FAA247", "#75D253", "#4988F2", "#BF4AEE", "#606060"};
    private static Logger logger = LoggerFactory.getLogger(CurriculumScheduleServiceImpl.class);
    @Autowired
    PkLessonInfoMapper pkLessonInfoMapper;
    @Autowired
    ClassInfoFeignClient classInfoFeignClient;

    @Override
    public List<PkLessonInfoDTO> getCurriculumSchedule(String userId, Boolean ifTeacher, Date before, Date after) {
        CurriculumScheduleQuery query = new CurriculumScheduleQuery();
        query.setUserId(userId);
        query.setBefore(getMondayOfThisWeek(before));
        query.setAfter(getSundayOfThisWeek(after));
        if (ifTeacher) {
            query.setUserType(PkLessonUserTypeEnum.TEACHER);
        } else {
            query.setUserType(PkLessonUserTypeEnum.STUDENT);
        }
        List<PkLessonInfoDTO> pkLessonInfos = pkLessonInfoMapper.getCurriculumSchedule(query);
        pkLessonInfos.parallelStream().forEach(p -> {
            Integer length = CURRICULUM_COLOR.length;
            Integer index = (p.getClassId() + length) % length;
            p.setColor(CURRICULUM_COLOR[index]);
            p.setExam(classInfoFeignClient.getSimpleClassInfo(p.getClassId()).getExam());
        });
        formatPkLessonInfo(pkLessonInfos);
        return pkLessonInfos;
    }

    @Override
    public List<TeacherCurriculumAO> getCurriculumScheduleForTeacher(String userId, Boolean ifTeacher, Date before, Date after, Boolean ifFull) {
        List<PkLessonInfoDTO> res = getCurriculumSchedule(userId, true, before, after);
        Date beforeDate = getMondayOfThisWeek(before);
        Date afterDate = getSundayOfThisWeek(after);
        res.forEach(p -> p.setExam(classInfoFeignClient.getSimpleClassInfo(p.getClassId()).getExam()));
        Map<Date, List<PkLessonInfoDTO>> dateMap = res.stream()
                .collect(Collectors.groupingBy(p -> DateUtils.truncate(p.getBeginTime(), Calendar.DAY_OF_MONTH)));
        List<TeacherCurriculumAO> curriculumAOs = new ArrayList<>();
        if (ifFull) {
            List<Date> dates = getBetween(beforeDate, afterDate);
            dates.forEach(
                    d -> {
                        TeacherCurriculumAO ao = new TeacherCurriculumAO();
                        ao.setDayDate(date2Str(d, FORMAT_DAY));
                        ao.setDayWeek(getFormatWeek(d));
                        if (dateMap.get(d) == null) {
                            ao.setLessons(new ArrayList<>());
                        } else {
                            ao.setLessons(dateMap.get(d));
                        }
                        curriculumAOs.add(ao);
                    }
            );
        } else {
            List<TeacherCurriculumAO> curriculumAOMaps = dateMap.entrySet().stream()
                    .sorted((o1, o2) -> {
                        if (o1.getKey().before(o2.getKey())) {
                            return -1;
                        } else {
                            return 1;
                        }
                    })
                    .map(e -> new TeacherCurriculumAO(date2Str(e.getKey(), FORMAT_DAY), getFormatWeek(e.getKey()), e.getValue()))
                    .collect(Collectors.toList());
            return curriculumAOMaps;
        }
        return curriculumAOs;
    }

    @Override
    public ClassCurriculumAO getCurriculumScheduleForClass(Integer classId, Date before, Date after, Boolean ifFull) {
        ClassCurriculumAO classCurriculumAO = new ClassCurriculumAO();
        ClassInfo classInfo = classInfoFeignClient.getSimpleClassInfo(classId);
        classCurriculumAO.setClassName(classInfo.getClassName());
        CurriculumScheduleQuery query = new CurriculumScheduleQuery();
        query.setClassId(classId);
        query.setBefore(getMondayOfThisWeek(before));
        query.setAfter(getSundayOfThisWeek(after));
        query.setUserType(PkLessonUserTypeEnum.TEACHER);
        List<PkLessonInfoDTO> pkLessonInfos = pkLessonInfoMapper.getCurriculumSchedule(query);
        pkLessonInfos.forEach(p -> p.setExam(classInfoFeignClient.getSimpleClassInfo(p.getClassId()).getExam()));
        if (!CollectionUtils.isEmpty(pkLessonInfos)) {
            //修改课节数据结构
            formatPkLessonInfoForClass(classCurriculumAO, pkLessonInfos, query.getBefore(), query.getAfter(), ifFull);
        }
        //科目-老师-课时信息
        //提取 科目 老师 教课时间 元素
        List<SubjectMessageAO> subjects = pkLessonInfos.stream()
                .map(p -> new SubjectMessageAO(p.getSubjectName(), p.getTeacherName(), getClassHour(p.getBeginTime(), p.getEndTime())))
                .collect(Collectors.toList());
        //进行分组  key:subject teacherName  value:List<SubjectMessage>
        Map<SubjectMessageAO, List<SubjectMessageAO>> subMap =
                subjects.stream().collect(Collectors.groupingBy(SubjectMessageAO::getGroupingBy));
        //计算 每组 总课时
        subMap.entrySet().forEach(
                e -> {
                    BigDecimal sum = e.getValue().stream().map(SubjectMessageAO::getClassHour)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    //将课时总数放到key对象中
                    e.getKey().setClassHour(sum);
                }
        );
        //对key 排序 然后分组
        Map<String, List<SubjectMessageAO>> groupMap = subMap.keySet().stream()
                .sorted((o1, o2) -> -o1.getClassHour().compareTo(o2.getClassHour()))
                .collect(Collectors.groupingBy(SubjectMessageAO::getSubjectName));
        List<SubjectMessageAO> subs = new ArrayList<>();
        //获取每组第一个
        groupMap.entrySet().forEach(
                e -> {
                    subs.add(e.getValue().get(0));
                }
        );
        classCurriculumAO.setSubjectMessages(subs);
        return classCurriculumAO;
    }

    private void formatPkLessonInfoForClass(ClassCurriculumAO classCurriculumAO, List<PkLessonInfoDTO> pkLessonInfos, Date before, Date after, Boolean ifFull) {
        //按开始时间排序
        pkLessonInfos.stream().sorted().forEach(
                p -> {
                    p.setLessonStatus(getFormatLessonStatus(p.getBeginTime(), p.getEndTime()));
                    p.setDateTimeType(getAmOrPm(p.getBeginTime()));
                    p.setTime(date2Str(p.getBeginTime(), FORMAT_MINUTE) + "~" + date2Str(p.getEndTime(), FORMAT_MINUTE));
                }
        );
        //按时间 进行分组
        Map<Date, List<PkLessonInfoDTO>> dateListMap = pkLessonInfos.stream()
                .collect(Collectors.groupingBy(p -> DateUtils.truncate(p.getBeginTime(), Calendar.DAY_OF_MONTH)));
        Map<Date, Map<String, List<PkLessonInfoDTO>>> amPmMap = new HashMap<>();
        dateListMap.entrySet().stream().forEach(
                e -> {
                    Map<String, List<PkLessonInfoDTO>> put = amPmMap.put(e.getKey(),
                            //上下午分组
                            e.getValue().stream().collect(Collectors.groupingBy(PkLessonInfoDTO::getDateTimeType)));
                }
        );
        List<ClassCurriculumAO.DayPkLessonInfo> dayPkLessonInfos = new ArrayList<>();
        if (ifFull) {
            List<Date> dates = getBetween(before, after);
            dates.forEach(
                    d -> {
                        ClassCurriculumAO.DayPkLessonInfo dayPkLessonInfo = classCurriculumAO.new DayPkLessonInfo();
                        dayPkLessonInfo.setDayDate(date2Str(d, FORMAT_DAY));
                        dayPkLessonInfo.setDayWeek(getFormatWeek(d));
                        if (amPmMap.get(d) == null) {
                            dayPkLessonInfo.setLessons(new HashMap<>());
                        } else {
                            dayPkLessonInfo.setLessons(amPmMap.get(d));
                        }
                        dayPkLessonInfos.add(dayPkLessonInfo);
                    }
            );
        } else {
            amPmMap.entrySet().stream()
                    .sorted((o1, o2) -> {
                        if (o1.getKey().before(o2.getKey())) {
                            return -1;
                        } else {
                            return 1;
                        }
                    })
                    .forEach(
                            e -> {
                                ClassCurriculumAO.DayPkLessonInfo dayPkLessonInfo = classCurriculumAO.new DayPkLessonInfo();
                                dayPkLessonInfo.setDayDate(date2Str(e.getKey(), FORMAT_DAY));
                                dayPkLessonInfo.setDayWeek(getFormatWeek(e.getKey()));
                                dayPkLessonInfo.setLessons(e.getValue());
                                dayPkLessonInfos.add(dayPkLessonInfo);
                            });
        }
        classCurriculumAO.setDayInfos(dayPkLessonInfos);

    }


    private BigDecimal getClassHour(Date before, Date after) {
        long time = after.getTime() - before.getTime();
        return new BigDecimal(time)
                .divide(new BigDecimal(1000).multiply(new BigDecimal(3600)), 2, BigDecimal.ROUND_HALF_UP);
    }

    private void formatPkLessonInfo(List<PkLessonInfoDTO> pkLessonInfos) {
        if (CollectionUtils.isEmpty(pkLessonInfos)) {
            return;
        }
        Date current = new Date();
        //根据班级进行分组 减少feign调用次数
        Map<Integer, List<PkLessonInfoDTO>> classKeyMap = pkLessonInfos.stream().collect(Collectors.groupingBy(PkLessonInfoDTO::getClassId));
        List<PkLessonInfoDTO> fullInfoList = new ArrayList<>();
        classKeyMap.entrySet().forEach(
                e -> {
                    ClassInfo classInfo = classInfoFeignClient.getSimpleClassInfo(e.getKey());
                    e.getValue().forEach(
                            p -> {
                                Date before = p.getBeginTime();
                                Date end = p.getEndTime();
                                p.setDay(date2Str(before, FORMAT_DAY));
                                p.setWeekDay(getFormatWeek(before));
                                p.setTime(date2Str(before, FORMAT_MINUTE) + "~" + date2Str(end, FORMAT_MINUTE));
                                p.setClassName(classInfo.getClassName());
                                if (current.after(DateUtils.addMinutes(before, -20)) && current.before(end)) {
                                    p.setIfGoingToClass(true);
                                    p.setUrlForClassIn("classInUrl");
                                }
                                p.setLessonStatus(getFormatLessonStatus(before, end));
                                p.setDayTime(getFormatDayTime(before));
                            }
                    );
                    //组合成新集合
                    fullInfoList.addAll(e.getValue());
                }
        );
    }


    private LessonStatusEnum getFormatLessonStatus(Date before, Date end) {
        Date current = new Date();
        if (current.before(before)) {
            return LessonStatusEnum.LESSON_UNSTART;
        } else if (current.before(end) && current.after(before)) {
            return LessonStatusEnum.LESSON_IMPLEMENTING;
        } else if (current.after(end)) {
            return LessonStatusEnum.LESSON_END;
        }
        return null;
    }


    private String getFormatDayTime(Date date) {
        Date day = DateUtils.truncate(date, Calendar.DAY_OF_MONTH);
        Date pointOne = DateUtils.addHours(day, 12);
        Date pointTwo = DateUtils.addHours(day, 18);
        if (date.before(pointOne)) {
            return DAY_TIME_MORNING;
        } else if (date.after(pointOne) && date.before(pointTwo)) {
            return DAY_TIME_AFTERNOON;
        } else if (date.after(pointTwo)) {
            return DAY_TIME_NIGHT;
        }
        return null;
    }
}
